package com.hongyun.tms.aspect;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.hongyun.tms.cache.CacheManager;
import com.hongyun.tms.common.PageResponseDto;
import com.hongyun.tms.common.annotation.Dept;
import com.hongyun.tms.common.annotation.Dict;
import com.hongyun.tms.common.annotation.Translate;
import com.hongyun.tms.common.constants.TranslateAnnotationConstant;
import com.hongyun.tms.domain.AjaxResult;
import com.hongyun.tms.domain.DictItemDomain;
import com.hongyun.tms.entity.Dock;
import com.hongyun.tms.entity.MStaff;
import com.hongyun.tms.entity.SysDept;
import com.hongyun.tms.entity.TmsMTug;
import com.hongyun.tms.service.IDockService;
import com.hongyun.tms.service.IMStaffService;
import com.hongyun.tms.service.SysDeptService;
import com.hongyun.tms.service.TmsMTugService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 当controller层  是 get请求  且  返回类型是AjaxResult时，触发翻译。之前的接口返回R类型的不会走
 * 字典翻译
 * 在方法返回结果后，对结果中的字段进行字典翻译。
 * 通过@Dict注解标记的字段，根据字典代码获取对应的翻译值，并添加到返回的JSON对象中。
 * <p>
 * 以下场景会处理：
 * 1. 集合：递归处理集合中的每个元素(如果list某个元素的字段包含了@Dict，则会处理)。
 * 2. 数组：递归处理数组中的每个元素。(如果array中某个元素的字段包含了@Dict，则会处理)
 * 3. 分页响应：递归处理分页响应中的记录。
 * 4. Map：通过 shouldTranslate()检查 Map 的值的类型是否包含 @Dict 注解的字段，决定是否递归处理.
 * 5. 自定义对象：只对包含 @Dict 注解的字段进行字典翻译。
 *
 * @author liPeng
 * @date 2024-06-06
 */
@Aspect
@Component
public class DictAspect {
    @Resource
    private CacheManager cacheManager;

    @Resource
    private IMStaffService staffService;

    @Resource
    private IDockService dockService;

    @Resource
    private TmsMTugService tugService;

    @Resource
    private SysDeptService deptService;

    // 只拦截所有标有@GetMapping注解的方法
    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
    public void getMapping() {
    }

    // 只拦截返回AjaxResult类型的方法
    @Pointcut("execution(public com.hongyun.tms.domain.AjaxResult *(..))")
    public void ajaxResultMethods() {
    }

    // 需要忽略处理的 URL列表，或者在下方 handle那里加个else if，加入对Map等返回类型的处理
    private final List<String> ignoredUrlPatterns = Arrays.asList(
            "/internal/public/getLocalSelect",
            "/internal/public/getRemoteSelect",
            "/firstPage/getPlanNum",
            "/firstPage/getFeeList",
            "/firstPage/getWorkItemGroup",
            "/screen/**"  // 使用通配符匹配所有以 /screen/ 开头的 URL
    );


    private String getCurrentRequestUrl() {
        // 使用 Spring 提供的工具来获取当前请求的 URL
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest().getRequestURI();
    }

    private boolean shouldIgnoreUrl(String url) {
        // 判断当前 URL 是否匹配任何忽略的模式
        return ignoredUrlPatterns.stream().anyMatch(pattern -> url.startsWith(pattern.replace("**", "")));
    }


    // 拦截返回AjaxResult类型的和R类型的方法
//    @Pointcut("execution(public com.qdport.tms.domain.AjaxResult *(..)) || execution(public com.qdport.core.tool.api.R *(..))")
//    public void ajaxOrResultMethods() {}
    // 在方法成功执行后执行，处理返回结果，且只处理返回AjaxResult类型的方法
    @AfterReturning(pointcut = "getMapping() && ajaxResultMethods()", returning = "result")
//    @AfterReturning(pointcut = "getMapping() && ajaxOrResultMethods()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) throws IllegalAccessException {
        // 获取当前请求的 URL
        String url = getCurrentRequestUrl();

        // 如果 URL 在忽略列表中，直接返回，不处理
        if (shouldIgnoreUrl(url)) {
            return;
        }

        if (result != null) {
            //
            // 1. 从返回结果中获取数据
            Object data = getDataFromResult(result);
            if (data != null) {
                // 2. 递归处理数据对象，进行字典翻译
                Object translatedData = handleDataRecursively(data);
                // 3. 将处理后的数据设置回返回结果
                setDataToResult(result, translatedData);
            }
        }
    }

    /**
     * 获取返回结果中的数据
     *
     * @param result 方法返回结果
     * @return 返回的数据对象
     */
    private Object getDataFromResult(Object result) {
        if (result instanceof AjaxResult) {
            return ((AjaxResult<?>) result).getData();
        }
        return null;
    }

    /**
     * 将处理后的数据设置回返回结果
     *
     * @param result 返回结果对象
     * @param data   处理后的数据
     */
    private void setDataToResult(Object result, Object data) throws IllegalAccessException {
        if (result instanceof AjaxResult) {
            AjaxResult ajaxResult = (AjaxResult) result;
            if (ajaxResult.getData() != null) {
                ajaxResult.setData(data);
            }
        }
    }

    /**
     * 递归处理数据对象，进行字典翻译
     *
     * @param data 原始数据对象
     * @return 处理后的数据对象
     */
    private Object handleDataRecursively(Object data) throws IllegalAccessException {
        if (data instanceof Collection) {
            // 1. 处理集合类型数据
            return ((Collection<?>) data).stream()
                    .map(item -> {
                        try {
                            return handleDataRecursively(item);
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    })
                    .collect(Collectors.toList());
        } else if (data.getClass().isArray()) {
            // 2. 处理数组类型数据
            return Arrays.stream((Object[]) data)
                    .map(item -> {
                        try {
                            return handleDataRecursively(item);
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    })
                    .toArray();
        } else if (data instanceof PageResponseDto) {
            // 3. 处理分页响应数据
            PageResponseDto<?> pageResponseDto = (PageResponseDto<?>) data;
            List<?> items = pageResponseDto.getRecords();
            if (CollectionUtils.isNotEmpty(items)) {
                List translatedItems = items.stream()
                        .map(item -> {
                            try {
                                return handleDataRecursively(item);
                            } catch (IllegalAccessException e) {
                                throw new RuntimeException(e);
                            }
                        })
                        .collect(Collectors.toList());
                pageResponseDto.setRecords(translatedItems);
            }
            return pageResponseDto;
        } else {
            // 4. 处理自定义对象
            return translateFields(data);
        }
    }

    /**
     * 对自定义对象进行字典翻译
     * 在DictAspect中，修改translateFields方法，跳过对@JsonFormat标记的字段进行处理
     *
     * @param data 自定义对象
     * @return 处理后的JSON对象
     */
    private JSONObject translateFields(Object data) throws IllegalAccessException {
        JSONObject jsonObject = new JSONObject();
        Class<?> tempClass = data.getClass();

        while (tempClass != null) {
            Field[] fields = tempClass.getDeclaredFields();
            for (Field field : fields) {
                if ("serialVersionUID".equals(field.getName())) {
                    continue;
                }
                field.setAccessible(true);

                // 检查是否是时间字段，避免覆盖 @JsonFormat 标记
                if (handleJsonFormatField(field, data, jsonObject)) {
                    continue; // 如果是 @JsonFormat 处理的字段，跳过后续处理
                }

                // 处理其他字段
                Object fieldValue = field.get(data);
                if (fieldValue instanceof List) {
                    List<?> listValue = (List<?>) fieldValue;
                    List<?> translatedList = listValue.stream()
                            .map(item -> {
                                try {
                                    return handleDataRecursively(item);
                                } catch (IllegalAccessException e) {
                                    throw new RuntimeException(e);
                                }
                            })
                            .collect(Collectors.toList());
                    jsonObject.put(field.getName(), translatedList);
                } else {
                    jsonObject.put(field.getName(), fieldValue);
                    // 字典翻译等其他处理
                    Dict dict = field.getAnnotation(Dict.class);
                    if (dict != null) {
                        translateField(data, field, jsonObject);
                    }
                    // 处理@Translate注解的字段
                    Translate translate = field.getAnnotation(Translate.class);
                    if (translate != null) {
                        translateUserField(field, fieldValue, jsonObject, translate);
                    }
                }
            }
            tempClass = tempClass.getSuperclass();
        }
        return jsonObject;
    }

    /**
     * 处理带有 @JsonFormat 注解的字段
     *
     * @param field      需要处理的字段
     * @param data       原始对象
     * @param jsonObject 用于存储结果的 JSONObject
     * @return 如果字段带有 @JsonFormat 注解并处理过，则返回 true，否则返回 false
     */
    private boolean handleJsonFormatField(Field field, Object data, JSONObject jsonObject) throws IllegalAccessException {
        JsonFormat jsonFormat = field.getAnnotation(JsonFormat.class);
        if (jsonFormat != null) {
            Object fieldValue = field.get(data);

            // 检查是否是时间字段且格式不为空
            if (fieldValue instanceof Date && StringUtils.isNotBlank(jsonFormat.pattern())) {
                Date date = (Date) fieldValue;
                SimpleDateFormat formatter = new SimpleDateFormat(jsonFormat.pattern());
                String formattedDate = formatter.format(date);
                jsonObject.put(field.getName(), formattedDate);  // 格式化日期
            } else {
                jsonObject.put(field.getName(), fieldValue);  // 保持时间字段原样
            }
            return true;
        }
        return false;
    }


    /**
     * 对字段进行字典翻译:@Dict
     *
     * @param data       原始数据对象
     * @param field      字段
     * @param jsonObject JSON对象
     */
    private void translateField(Object data, Field field, JSONObject jsonObject) throws IllegalAccessException {
        Dict dict = field.getAnnotation(Dict.class);
        if (dict != null) {
            Object fieldValue = field.get(data);
            if (fieldValue != null) {
                // 从缓存中获取字典项
                List<DictItemDomain> cacheList = cacheManager.getDictCache();
                cacheList = cacheList.stream()
                        .filter(dictCache -> dictCache.getDictType().equals(dict.code()) && dictCache.getDictValue().equals(fieldValue.toString()))
                        .collect(Collectors.toList());
                String dictLabel = CollectionUtils.isNotEmpty(cacheList) ? cacheList.get(0).getDictLabel() : null;
                String translatedFieldName = StringUtils.isBlank(dict.fieldName()) ? field.getName() + "Str" : dict.fieldName();
                // 将翻译后的值放入JSON对象
                jsonObject.put(translatedFieldName, dictLabel);
            }
        }
    }

    /**
     * 对@Translate注解的字段进行翻译
     *
     * @param field               字段
     * @param fieldValue          字段值
     * @param jsonObject          JSON对象
     * @param translateAnnotation @Translate注解
     */
    private void translateUserField(Field field, Object fieldValue, JSONObject jsonObject, Translate translateAnnotation) throws IllegalAccessException {
        if (fieldValue != null) {
            // 从缓存中获取用户名
            String userName = cacheManager.getUsernameCache(translateAnnotation.tableName() + fieldValue);
            if (StringUtils.isBlank(userName)) {
                // 从数据库中获取用户名
                userName = fetchUserNameFromUserId((Long) fieldValue, translateAnnotation.tableName());
                // 将用户名缓存
                cacheManager.cacheUserInfo(translateAnnotation.tableName() + fieldValue, userName);
            }
            String userFieldName = StringUtils.isBlank(translateAnnotation.fieldName()) ? field.getName() + "Name" : translateAnnotation.fieldName();
            jsonObject.put(userFieldName, userName);
        }
    }

    /**
     * 根据UserId获取用户名
     *
     * @param fieldValue UserId
     * @param tableName  表名
     * @return 用户名
     */
    private String fetchUserNameFromUserId(Long fieldValue, String tableName) {
        switch (tableName) {
            case TranslateAnnotationConstant.TABLE_STAFF:
                return Optional.ofNullable(staffService.getById(fieldValue)).map(MStaff::getStaffName).orElse("");
            case TranslateAnnotationConstant.TABLE_TMS_DOCK:
                return Optional.ofNullable(dockService.getById(fieldValue)).map(Dock::getDockName).orElse("");
            case TranslateAnnotationConstant.TMS_M_TUG:
                return Optional.ofNullable(tugService.getById(fieldValue)).map(TmsMTug::getTugName).orElse("");
            default:
                return null;
        }
    }

    /**
     * 翻译@Dept
     *
     * @param field          result类data返回的字段
     * @param fieldValue     deptId
     * @param jsonObject     AjaxResult的data
     * @param deptAnnotation @Dept
     */

    private void translateDeptField(Field field, Object fieldValue, JSONObject jsonObject, Dept deptAnnotation) {
        if (fieldValue != null) {
            String deptName = cacheManager.getDeptNameCache(fieldValue.toString());
            if (StringUtils.isBlank(deptName)) {
                deptName = fetchDeptNameFromDeptId((Long) fieldValue, deptAnnotation.tableName());
                cacheManager.cacheDeptInfo(fieldValue.toString(), deptName);
            }
            String deptFieldName = StringUtils.isBlank(deptAnnotation.fieldName()) ? field.getName() + "Name" : deptAnnotation.fieldName();
            jsonObject.put(deptFieldName, deptName);
        }
    }

    private String fetchDeptNameFromDeptId(Long fieldValue, String tableName) {
        if (TranslateAnnotationConstant.TABLE_T_SYS_DEPT.equals(tableName)) {
            return Optional.ofNullable(deptService.getById(fieldValue)).map(SysDept::getName).orElse("");
        }
        return null;
    }
}
